#ifndef __METRIC_REGISTRY_HPP__
#define __METRIC_REGISTRY_HPP__

#include <string>
#include <map>
#include <mutex>
#include <memory>
#include <list>
#include <functional>

#include "metrics/Family.hpp"

namespace metrics
{
    class Counter;
    class Gauge;
    class Histogram;

    class MetricRegistry : public Collectable
    {
    private:
        mutable std::mutex _mutex;
        std::vector<std::shared_ptr<Family<Counter>>> _counters = {};
        std::vector<std::shared_ptr<Family<Gauge>>> _gauges = {};
        std::vector<std::shared_ptr<Family<Histogram>>> _histograms = {};

    private:
        template <typename T>
        std::vector<std::shared_ptr<Family<T>>> &families();

    public:
        MetricRegistry();
        ~MetricRegistry();

        /**
         * @brief 构造函数
         * 
         * @tparam T metric 类型
         * @param name metric 名字
         * @param help metric帮助信息
         * @param labels 标签
         * @return std::shared_ptr<Family<T>> metric族
         */
        template <typename T>
        std::shared_ptr<Family<T>> add(const std::string &name, const std::string &help, const std::map<std::string, std::string> &labels);

        /**
         * @brief 收集metric
         * 
         * @return std::vector<MetricFamily> 收集到的metric
         */
        std::vector<MetricFamily> collect() const;
    };

    template <typename T>
    std::shared_ptr<Family<T>> MetricRegistry::add(const std::string &name, const std::string &help, const std::map<std::string, std::string> &labels)
    {
        auto same_name_and_labels =
            [&name, &labels](const std::shared_ptr<Family<T>> &family)
        {
            return std::tie(name, labels) == std::tie(family->name(), family->labels());
        };

        //must be ref
        std::vector<std::shared_ptr<Family<T>>> &fs = families<T>();
        auto it = std::find_if(fs.begin(), fs.end(), same_name_and_labels);
        if (it != fs.end())
        {
            return *it;
        }

        auto family = std::make_shared<Family<T>>(name, help, labels);
        fs.push_back(family);
        return family;
    }

} // namespace metrics
#endif // __METRIC_REGISTRY_HPP__